/*
 * ProcessWrapper.java
 * 
 * Copyright 2007 Kamen Petroff.
 * All Rights Reserved.
 * 
 * This software is the proprietary information of Kamen Petroff.
 * Use is subject to license terms.
 * 
 * Created on Mar 2, 2007 at 11:13:47 PM by Kamen Petroff
 */
package com.google.code.shell4eclipse.internal.process;

import java.io.IOException;
import java.io.InputStream;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;

import com.google.code.shell4eclipse.Shell4EclipsePlugin;
import com.google.code.shell4eclipse.process.IProcess;
import com.google.code.shell4eclipse.process.IProcessListener;

/**
 * 
 *
 * @author Kamen Petroff 
 * @since Mar 2, 2007
 */
public class ProcessWrapper implements IProcess {

	private IProcessListener listener;
	private Process process;
	private InputStreamReader outReader;
	private InputStreamReader errReader;
	private ShutdownHook shutdownHook;
	private ProcessBuilder processBuilder;
	private IStreamListener errListener;
	private IStreamListener outListener;

	
	public ProcessWrapper (ProcessBuilder processBuilder) {
		this.processBuilder = processBuilder;
	}
	
	public void setProcessListener(IProcessListener listener) {
		this.listener = listener;
	}
	public void setErrorStreamListener(IStreamListener errListener) {
		this.errListener = errListener;
		if(errReader != null) {
			errReader.setListener(errListener);
		}
	}
	public void setOutputStreamListener(IStreamListener outListener) {
		this.outListener = outListener;
		if(outReader != null) {
			outReader.setListener(outListener);
		}
	}
	
	public InputStream getStdIn() {
		return process.getInputStream();
	}

	public void writeStdIn(byte[] buffer, int len) {
		try {
			process.getOutputStream().write(buffer, 0, len);
			process.getOutputStream().flush();
		} catch (IOException ex) {
			Shell4EclipsePlugin.log(IStatus.ERROR, "faled to write to shell process's STDOUT", ex);
		}
	}
	
	public void start() {
		if(processBuilder.directory() == null) {
			processBuilder.directory(ResourcesPlugin.getWorkspace().getRoot().getFullPath().toFile());
		}
		try {
			this.process = processBuilder.start();
			new ProcessTerminateHook().start();
			
			this.outReader = new InputStreamReader(process.getInputStream(), outListener);
			this.errReader = new InputStreamReader(process.getErrorStream(), errListener);
			
			outReader.start();
			errReader.start();
		} catch (IOException ex) {
			Shell4EclipsePlugin.log(IStatus.ERROR, "shell4eclipse has failed to start the underlying shell process", ex);
		}
	}
	
	private class ShutdownHook extends Thread {
		@Override
		public void run() {
			if(process != null) {
				listener = null;
				process.destroy();
			}
		}
	} // executed when the JVM shuts down
	
	public void kill() {
		if(process != null) {
			process.destroy();
		}
	}
	
	private class ProcessTerminateHook extends Thread {
		@Override
		public void run() {
			int exitValue = -1;
			try{
				shutdownHook = new ShutdownHook();
				Runtime.getRuntime().addShutdownHook(shutdownHook);
				exitValue = process.exitValue();
				if(listener != null) {
					listener.onProcessExited(exitValue);
				}
			}catch(IllegalThreadStateException ex) {
				for(;;) {
					try {
						exitValue = process.waitFor();
						break;
					} catch (InterruptedException ex1) {
					}
				}
			} finally {
				if(listener != null) {
					listener.onProcessExited(exitValue);
				}
				Runtime.getRuntime().removeShutdownHook(shutdownHook);
			}
		}
	}
}
